﻿#include "threadfileio.h"
#include <QFile>
#include <QFileInfo>
#include "utils.h"
#include <QThread>
#include <QMetaObject>

ThreadFileIO::ThreadFileIO(const QString &remoteFilePath,const QString &localFilePath, const QString &IP, uint port,uint threadType , QObject *parent)
    :QObject(parent),
    m_remoteFilePath(remoteFilePath),
    m_localFilePath(localFilePath),
    m_IP(IP),
    m_port(port)
{
    m_tcpsocket = new QTcpSocket(this);
    connect(m_tcpsocket,&QTcpSocket::readyRead , this, &ThreadFileIO::recv_Msg);

    switch(threadType){
    case ENUM_THREAD_TYPE_UPLOAD_FILE:
        // 连接后开始发起文件上传请求
        connect(m_tcpsocket,&QTcpSocket::connected , this, &ThreadFileIO::slot_uploadFileRequest);
        break;
    case ENUM_THREAD_TYPE_DOWNLOAD_FILE:
        // 连接后开始发起文件下载请求
        connect(m_tcpsocket,&QTcpSocket::connected , this, &ThreadFileIO::slot_downloadFileRequest);
        break;
    default:
        break;
    }
}

ThreadFileIO::~ThreadFileIO()
{
    if(file){
        if(file->isOpen())
            file->close();
        delete file;
    }
    if(fileHash) delete fileHash;
    if(m_tcpsocket->state() != QTcpSocket::UnconnectedState){
        m_tcpsocket->disconnectFromHost();
        if(m_tcpsocket->state() != QTcpSocket::UnconnectedState)
            m_tcpsocket->waitForDisconnected();
    }
    m_tcpsocket->deleteLater();
    MYLOG<<"完成析构！";
}

void ThreadFileIO::handleUploadingFile(PDU *pdu)
{
    if(strcmp(pdu->caData,UPLOAD_FILE_FAILED)== 0){
        emit signal_UploadFileStatus(UPLOAD_FILE_FAILED);
        return;
    }

    QFile file(m_localFilePath);
    if (!file.open(QIODevice::ReadOnly)) {
        MYLOG<<QThread::currentThreadId()<<": 打开文件失败";
        return;
    }
    int maxLenth = 4096;
    char* buffer = new char[maxLenth];
    qint64 bytesRead = 0;
    qint64 fileSize = file.size();
    qint64 hasSendSize = 0;
    emit signal_setProgeressBarMaxValue(fileSize);
    while ((bytesRead = file.read(buffer, maxLenth)) > 0) {
        PDU* pduData = mkPDU(bytesRead);
        pduData->uiMsgType = ENUM_MSG_TYPE_UPLOADING_FILE_REQUEST;
        memcpy(pduData->csMsg,buffer,bytesRead);
        m_tcpsocket->write((char* )pduData,pduData->uiPDULen);
        free(pduData);
        pduData = NULL;
        m_tcpsocket->waitForBytesWritten();
        hasSendSize += bytesRead;

        double progress = static_cast<double>(hasSendSize) / fileSize;
        if(progress > nextThreshold || hasSendSize == fileSize){
            nextThreshold += 0.05;
            emit signal_setProgeressBarValue(hasSendSize);
            // QMetaObject::invokeMethod(Base,
            //                           "slot_setProgressBarValue",
            //                           Qt::QueuedConnection,
            //                           Q_ARG(qint64, hasSendSize));
        }
    }
    file.close();
}

void ThreadFileIO::handleUploadedFile(PDU *pdu)
{
    MYLOG<<pdu->caData;
    emit signal_UploadFileStatus(pdu->caData);
    QThread::currentThread()->quit();
}

void ThreadFileIO::handleDownloadFileRespond(PDU *pdu)
{
    if(strcmp(pdu->caData,DOWNLOAD_FILE_FAILED) == 0){
        // 文件下载失败
        emit signal_DownloadFileStatus(DOWNLOAD_FILE_FAILED);
    }else if(strcmp(pdu->caData,DOWNLOAD_FILE_START) == 0){
        // 开始下载文件请求
        memcpy(&fileSize,pdu->csMsg,sizeof(qint64));

        // 初始化相关设置
        emit signal_setProgeressBarMaxValue(fileSize);
        fileHash = new QCryptographicHash(QCryptographicHash::Md5);
        nextThreshold = 0.1;
        m_iRecved = 0;
        file = new QFile(m_localFilePath);

        if(file->open(QIODevice::WriteOnly)){
            qint64 strSize = m_remoteFilePath.toStdString().size() + 1;
            uint uiMsgLen = sizeof(qint64) + strSize;// 字符串大小所占字节，字符串所占字节
            PDU* resPdu = mkPDU(uiMsgLen);
            resPdu->uiMsgType = ENUM_MSG_TYPE_DOWNLOAD_FILE_START_REQUEST;// 下载文件开始请求
            char* ptr = resPdu->csMsg;
            memcpy(ptr,&strSize, sizeof(qint64));
            ptr += sizeof(qint64);
            memcpy(ptr, m_remoteFilePath.toStdString().c_str(),strSize);
            m_tcpsocket->write((char*) resPdu,resPdu->uiPDULen);
        }else{
            emit signal_DownloadFileStatus(DOWNLOAD_FILE_FAILED);
        }
    }
}

void ThreadFileIO::handleDownloadFileSendRespond(PDU *pdu)
{
    if(strcmp(pdu->caData,DOWNLOAD_FILE_OK) == 0){
        qint64 strSize = 0;
        char* ptr = pdu->csMsg;
        memcpy(&strSize,ptr,sizeof(qint64));
        char* strMD5 = new char[strSize];
        ptr += sizeof(qint64);
        memcpy(strMD5,ptr,strSize);
        QString receiveFileMD5 = QString(fileHash->result().toHex());

        MYLOG<<"MD5:"<<strMD5<<receiveFileMD5;
        if(m_iRecved == fileSize && strMD5 == receiveFileMD5){
            emit signal_DownloadFileStatus(DOWNLOAD_FILE_OK);
        }else{
            emit signal_DownloadFileStatus(DOWNLOAD_FILE_FAILED);
        }
        file->close();
        QThread::currentThread()->quit();

    }else{
        char* buff = new char[pdu->uiMsgLen];
        memset(buff,0,pdu->uiMsgLen);
        memcpy(buff,pdu->csMsg,pdu->uiMsgLen);
        fileHash->addData(buff,pdu->uiMsgLen);
        qint64 m_iRecvedTemp = file->write(buff,pdu->uiMsgLen);
        m_iRecved += m_iRecvedTemp;
        double progress = static_cast<double>(m_iRecved) / fileSize;
        if(progress >= nextThreshold || m_iRecved == fileSize){
            nextThreshold += 0.1;
            emit signal_setProgeressBarValue(m_iRecved);
        }
    }
}

void ThreadFileIO::slot_connectToHost()
{
    MYLOG<<"开始运行！";
    if(m_tcpsocket->state() == QAbstractSocket::UnconnectedState){
        m_tcpsocket->connectToHost(m_IP,m_port);

        // m_tcpsocket->connectToHost("hb.frp.one", 34118);
    }
}

void ThreadFileIO::slot_uploadFileRequest()
{
    int index = m_remoteFilePath.lastIndexOf('/');
    QString strFileName =
        m_remoteFilePath.right(m_remoteFilePath.size() - index - 1);
    QString strCurPath = m_remoteFilePath.left(index);
    MYLOG<<"strCurPath:"<<strCurPath<<"strFileName"<<strFileName;
    QFile file(m_localFilePath);
    qint64 fileSize = file.size();
    PDU *pdu = mkPDU(strCurPath.size() + 1);
    pdu->uiMsgType = ENUM_MSG_TYPE_UPLOAD_FILE_REQUEST;
    // 存放当前路径
    memcpy(pdu->csMsg, strCurPath.toStdString().c_str(), strCurPath.toStdString().size());

    memcpy(pdu->caData,strFileName.toStdString().c_str(),32);
    memcpy(pdu->caData+32,&fileSize,sizeof(qint64));

    m_tcpsocket->write((char *)pdu, pdu->uiPDULen);
    free(pdu);
    pdu = NULL;

}

void ThreadFileIO::slot_downloadFileRequest()
{
    qint64 strSize = m_remoteFilePath.toStdString().size() + 1;
    uint uiMsgLen = sizeof(qint64) + strSize;// 字符串大小所占字节，字符串所占字节

    PDU* pdu = mkPDU(uiMsgLen);
    pdu->uiMsgType = ENUM_MSG_TYPE_DOWNLOAD_FILE_REQUEST;

    char* ptr = pdu->csMsg;
    memcpy(ptr,&strSize, sizeof(qint64));
    ptr += sizeof(qint64);
    memcpy(ptr, m_remoteFilePath.toStdString().c_str(),strSize);

    m_tcpsocket->write((char*)pdu,pdu->uiPDULen);
    free(pdu);
    pdu = NULL;
}

void ThreadFileIO::recv_Msg()
{
    static QByteArray buffer;
    buffer.append(m_tcpsocket->readAll());
    while(true){
        if((size_t)buffer.size() < sizeof(uint)){
            return;
        }
        uint uiPDULen = 0;
        uint uiMsgLen = 0;
        memcpy(&uiPDULen , buffer.constData(), sizeof(uint));

        uiMsgLen = uiPDULen - sizeof(uint);

        if((size_t)buffer.size() < uiPDULen){
            return;
        }

        PDU * pdu = mkPDU(uiMsgLen);
        memcpy((char*)pdu, buffer.constData(),uiPDULen);
        buffer.remove(0,uiPDULen);

        switch (pdu->uiMsgType) {
        case ENUM_MSG_TYPE_UPLOAD_FILE_RESPOND:
            handleUploadingFile(pdu);
            break;
        case ENUM_MSG_TYPE_UPLOADED_FILE_RESPOND:
            handleUploadedFile(pdu);
            break;
        case ENUM_MSG_TYPE_DOWNLOAD_FILE_RESPOND:
            handleDownloadFileRespond(pdu);
            break;
        case ENUM_MSG_TYPE_DOWNLOAD_FILE_SEND_RESPOND:
            handleDownloadFileSendRespond(pdu);
            break;
        default:
            break;
        }
        free(pdu);
        pdu = NULL;

    }
}
